// 画白色方块
function drawScene(
  gl,
  programInfo,
  buffers,
  camera,
  isInverting,
  showTraversal,
  showRemaining
) {
  gl.enable(gl.DEPTH_TEST) // Enable 深度測試
  gl.depthFunc(gl.ALWAYS) // 近处的事物掩盖了远处的事物

  // 告诉 WebGL 如何从位置中拉出位置
  // 缓冲区添加到 vertexPosition 属性中。
  {
    const numComponents = 2 // 每次迭代拉出 2 个值
    const type = gl.FLOAT // 缓冲区中的数据是 32 位浮点数
    const normalize = false // 不要规范化
    const stride = 0 // 从一组值到下一组值获取多少字节
    // 0 = 使用上面的 type 和 numComponents
    const offset = 0 // 缓冲区内的字节数
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position)
    gl.vertexAttribPointer(
      programInfo.attribLocations.vertexPosition,
      numComponents,
      type,
      normalize,
      stride,
      offset
    )
    gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition)
  }

  // 告诉 WebGL 在绘图时使用我们的程序

  gl.useProgram(programInfo.program)

  gl.uniformMatrix4fv(
    programInfo.uniformLocations.projectionMatrix,
    false,
    camera.perspective
  )
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.modelViewMatrix,
    false,
    camera.view
  )
  gl.uniform1i(programInfo.uniformLocations.isInverting, isInverting)
  gl.uniform1i(programInfo.uniformLocations.showTraversal, showTraversal)
  gl.uniform1i(programInfo.uniformLocations.showRemaining, showRemaining)

  {
    const offset = 0
    const vertexCount = 4
    gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount)
  }
}

function initBuffers(gl, width, height) {
  // 建立一個 buffer 來儲存正方形的座標

  const positionBuffer = gl.createBuffer()

  // 选择 positionBuffer 作为要应用缓冲区的
  // 操作从这里开始。

  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)

  // 现在为正方形创建一个位置数组。
  const positions = [0, 0, width, 0, 0, -height, width, -height]

  // 现在将位置列表传递到 WebGL 中以构建
  // 形状。为此，我们从
  // JavaScript 数组，然后使用它来填充当前缓冲区。

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)

  return {
    position: positionBuffer,
  }
}
function loadShader(gl, type, source) {
  const shader = gl.createShader(type)

  // 将源发送到着色器对象

  gl.shaderSource(shader, source)

  // 编译着色器程序

  gl.compileShader(shader)

  // 查看编译是否成功

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.error(
      `An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`
    )
    gl.deleteShader(shader)
    return null
  }

  return shader
}

//
// 初始化 shader 來告知WebGL怎麼畫
//
function initShaderProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource)
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource)

  // 建立 shader 程式
  const shaderProgram = gl.createProgram()
  gl.attachShader(shaderProgram, vertexShader)
  gl.attachShader(shaderProgram, fragmentShader)
  gl.linkProgram(shaderProgram)

  // 錯誤處理
  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    console.error(
      `Unable to initialize the shader program: ${gl.getProgramInfoLog(
        shaderProgram
      )}`
    )
    return null
  }

  return shaderProgram
}

const defaultVsSource = `
  attribute vec4 aVertexPosition;

  uniform mat4 uModelViewMatrix;
  uniform mat4 uProjectionMatrix;

  void main() {
    gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
  }
`

const defaultFsSource = `
  precision mediump float;
  uniform bool isInverting;
  void main() {
    if (isInverting) {
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    } else {
      gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
  }
`

const generateProgramInfo = (
  gl,
  vsSource = defaultVsSource,
  fsSource = defaultFsSource
) => {
  const shaderProgram = initShaderProgram(gl, vsSource, fsSource)
  const programInfo = {
    program: shaderProgram,
    attribLocations: {
      vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
    },
    uniformLocations: {
      projectionMatrix: gl.getUniformLocation(
        shaderProgram,
        "uProjectionMatrix"
      ),
      modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
      isInverting: gl.getUniformLocation(shaderProgram, "isInverting"),
      showTraversal: gl.getUniformLocation(shaderProgram, "showTraversal"),
      showRemaining: gl.getUniformLocation(shaderProgram, "showRemaining"),
    },
  }
  return programInfo
}
